﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Reflection;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.IdGenerators;

namespace Charisma.MongoDB
{
    public sealed class IdGeneratorConvention : IIdGeneratorConvention
    {
        private KeyGeneratorKind kind;
        private readonly HashSet<Type> types;

        public IdGeneratorConvention(HashSet<Type> types, KeyGeneratorKind kind)
        {
            Contract.Requires(types != null);

            this.types = types;
            this.kind = kind;
        }

        public IIdGenerator GetIdGenerator(MemberInfo memberInfo)
        {
            var memberType = BsonClassMap.GetMemberInfoType(memberInfo);
            if (!types.Contains(memberInfo.DeclaringType))
                kind = KeyGeneratorKind.Skip;
            switch (kind)
            {
                case KeyGeneratorKind.BuiltIn:
                    if (memberType == typeof(string))
                        return StringObjectIdGenerator.Instance;
                    if (memberType == typeof(ObjectId) || memberType == typeof(object))
                        return ObjectIdGenerator.Instance;
                    if (memberType == typeof(Guid))
                        return CombGuidGenerator.Instance;
                    // TODO: create long and int generators
                    throw new InvalidOperationException(
                        string.Format("There is no Key generator for '{0}' in MongoDB. Member: '{1}.{2}'", 
                            memberType, 
                            memberType.DeclaringType, 
                            memberType.Name));
                case KeyGeneratorKind.RequireExplicitKey:
                    if (memberType.IsValueType)
                    {
                        var zeroIdCheckerDefinition = typeof(ZeroIdChecker<>);
                        var zeroIdCheckerType = zeroIdCheckerDefinition.MakeGenericType(memberType);
                        return (IIdGenerator) Activator.CreateInstance(zeroIdCheckerType);
                    }
                    return NullIdChecker.Instance;
                case KeyGeneratorKind.Skip:
                    return null;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}